package com.leinao.git;

import com.leinao.commons.Response;
import com.leinao.constant.Constant;
import com.leinao.constant.FileErrorEnum;
import com.leinao.file.dto.FileInfoDto;
import com.leinao.file.dto.FilePageDto;
import org.apache.commons.lang.ArrayUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 *
 * @Description Git业务操作类
 * @author lizi
 * @date 2019/05/17 下午6:19
 * @Version 1.0
 */
@Component
public class GitService {

	private static final Logger LOGGER = LoggerFactory.getLogger(GitService.class);

	@Autowired
	private JGitService jGitService;

	/**
	 * 初始化代码仓库
	 *
	 * @param userId 用户ID
	 * @param path 代码路径 /projects/${userId}/${projectName}/code
	 * @param projectName 工程名
	 * @param timestamp : 时间戳，可以用来保证代码仓库名唯一
	 */
	public Response<String> initRepository(String userId, String path, String projectName, String timestamp) {
		long now = System.currentTimeMillis();
		LOGGER.info("init repository param userId : {}, path : {}, projectName : {}", userId, path, projectName);

		// git目录
		String gitPath = path.substring(0, path.length() - Constant.PROJECT_CODE_STRUCTURE.length());

		// 代码路径仓库初始化
		boolean initRepository = jGitService.init(gitPath);

		// 创建远程仓库
		String repository = null;
		if(initRepository) {
			String repositoryName = userId + "/" + projectName + "-" + timestamp;
			repository = jGitService.createRemoteRepository(repositoryName);
		}
		initRepository = repository != null;

		// 添加远程仓库
		if(initRepository) {
			initRepository = jGitService.addRemoteRepository(gitPath, repository);
		}

		// 提交代码到master分支
		if(initRepository) {
			initRepository = jGitService.commitAndPush(gitPath, Constant.GIT_CODE_STRUCTURE, "init repository", true);
		}

		if(initRepository) {
			LOGGER.info("init repository projectName : {}, spend time : {}s：", projectName, (System.currentTimeMillis()  - now) / 1000);
			return Response.ok(repository);
		} else {
			LOGGER.info("init repository failure, projectName: {}", projectName);
			return Response.error(Constant.RESPONSE_STATUS_SUCCESS, FileErrorEnum.REPOSITORY_INIT_FAILURE.getSeq(), FileErrorEnum.REPOSITORY_INIT_FAILURE.getDesc(), null);
		}
	}

	/**
	 * 代码仓库添加标签
	 *
	 * @param path 代码路径 /projects/${userId}/${projectName}/code
	 * @param version 分支tag版本
	 */
	public Response<String> initTag(String path, String version) {
		long now = System.currentTimeMillis();
		LOGGER.info("init tag param path : {}, version : {}", path, version);

		// git目录
		String gitPath = path.substring(0, path.length() - Constant.PROJECT_CODE_STRUCTURE.length());

		// 提交代码到master分支
		boolean initTag = jGitService.commitAndPush(gitPath, Constant.GIT_CODE_STRUCTURE, "commit code for tag " + version, true);

		// 新建tag
		if(initTag) {
			initTag = jGitService.createTag(gitPath, version);
		}

		if(initTag) {
			LOGGER.info("init tag path : {}, spend time : {}s：", path, (System.currentTimeMillis()  - now) / 1000);
			return Response.ok(version);
		} else {
			LOGGER.info("init tag failure, path: {}", path);
			return Response.error(Constant.RESPONSE_STATUS_SUCCESS, FileErrorEnum.TAG_CREATE_FAILURE.getSeq(), FileErrorEnum.TAG_CREATE_FAILURE.getDesc(), null);
		}
	}

	/**
	 * 代码克隆到自己的项目代码目录下，并重新初始化仓库
	 *
	 * @param userId 用户ID
	 * @param path 代码克隆到的目录 /projects/${userId}/${projectName}/code
	 * @param projectName 工程名
	 * @param timestamp : 时间戳，可以用来保证代码仓库名唯一
	 * @param repository 工程仓库地址
	 * @param masterTag 主分支使用的代码标签
	 * @param tagList 代码标签列表
	 */
	public Response<String> cloneNewRepository(String userId, String path, String projectName, String timestamp, String repository, String masterTag, List<String> tagList) {
		long now = System.currentTimeMillis();
		LOGGER.info("clone new repository param userId : {}, path : {}, projectName : {}, repository : {}, masterTag : {}, tagList : {}", userId, path, projectName, repository, masterTag, tagList);

		// git目录
		String gitPath = path.substring(0, path.length() - Constant.PROJECT_CODE_STRUCTURE.length());

		// 克隆代码
		boolean cloneNewRepository = jGitService.clone(gitPath, repository);

		// 移除仓库信息
		if(cloneNewRepository) {
			cloneNewRepository = jGitService.removeRemoteRepository(gitPath);
		}

		// 创建远程仓库
        String newRepository = null;
        if (cloneNewRepository) {
            String repositoryName = userId + "/" + projectName + "-" + timestamp;
            newRepository = jGitService.createRemoteRepository(repositoryName);
        }
        cloneNewRepository = newRepository != null;

		// 添加远程仓库
        if (cloneNewRepository) {
            cloneNewRepository = jGitService.addRemoteRepository(gitPath, newRepository);
        }

		// 删除除了master的所有分支
        if (cloneNewRepository) {
            cloneNewRepository = jGitService.keepBranch(gitPath, Collections.singletonList("master"));
        }

		// 删除指定tag集合外的所有任务
        if (cloneNewRepository) {
            cloneNewRepository = jGitService.keepTag(gitPath, tagList);
        }

		// master分支使用某个标签的代码
        if (cloneNewRepository) {
            cloneNewRepository = jGitService.resetTag(gitPath, masterTag);
        }

		// 提交代码到master分支
		if (cloneNewRepository) {
			cloneNewRepository = jGitService.push(gitPath);
		}

		if (cloneNewRepository) {
			LOGGER.info("clone new repository newRepository : {}, spend time : {}s：", newRepository, (System.currentTimeMillis() - now) / 1000);
			return Response.ok(newRepository);
		} else {
			LOGGER.info("clone new repository failure, repository: {}", newRepository);
			return Response.error(Constant.RESPONSE_STATUS_SUCCESS, FileErrorEnum.REPOSITORY_INIT_FAILURE.getSeq(), FileErrorEnum.REPOSITORY_INIT_FAILURE.getDesc(), null);
		}
	}

	/**
	 * @Description 遍历远程仓库某个分支下的所有文件
	 * @param repository 远程仓库地址 git@github.com:${userId}/project1-123123423423423.git
	 * @param tagName 标签名
	 * @param dir 搜索路径，空字符串表示树的根
	 * @param pageNumber: 当前页数
	 * @param pageSize: 每页条数
	 * @author suqiliang
	 * @Date 2018年6月29日 上午11:56:52
	 */
	public FilePageDto getRemoteFileList(String repository, String tagName, String dir, Integer pageNumber, Integer pageSize) {
		// 文件路径处理
		if(StringUtils.isEmpty(dir)) {
			dir = Constant.GIT_CODE_STRUCTURE;
		} else {
			dir = Constant.GIT_CODE_STRUCTURE + (dir.startsWith(File.separator) ? dir : File.separator + dir);
		}

		// 获取git远程仓库下的文件列表
		List<FileInfoDto> fileList = jGitService.getRemoteFileLst(repository, tagName, dir);

		return transferFileListToPage(fileList, pageNumber, pageSize);
	}

	/**
	 * @Description 遍历本地仓库某个分支下的所有文件
	 * @param localPath /projects/${userId}/${projectName}/code
	 * @param tagName 标签名
	 * @param dir 搜索路径，空字符串表示树的根
	 * @param pageNumber: 当前页数
	 * @param pageSize: 每页条数
	 * @author suqiliang
	 * @Date 2018年6月29日 上午11:56:52
	 */
	public FilePageDto getLocalFileList(String localPath, String tagName, String dir, Integer pageNumber, Integer pageSize) {
		// git目录
		String gitPath = localPath.substring(0, localPath.length() - Constant.PROJECT_CODE_STRUCTURE.length());

		// 文件路径处理
		if(StringUtils.isEmpty(dir)) {
			dir = Constant.GIT_CODE_STRUCTURE;
		} else {
			dir = Constant.GIT_CODE_STRUCTURE + (dir.startsWith(File.separator) ? dir : File.separator + dir);
		}

		// 获取git仓库下的文件列表
		List<FileInfoDto> fileList = jGitService.getLocalFileList(gitPath, tagName, dir);

		return transferFileListToPage(fileList, pageNumber, pageSize);
	}

	/**
	 * @Description 远程仓库文件预览
	 * @param repository 远程仓库地址 git@github.com:${userId}/project1-123123423423423.git
	 * @param tagName 标签名
	 * @param dir 搜索路径，空字符串表示树的根
	 * @author lizi
	 * @date 2018/12/18 下午6:43
	 */
	public void previewRemoteFile(String repository, String tagName, String dir, HttpServletResponse response) {
		try {
			// 文件路径处理
			dir = Constant.GIT_CODE_STRUCTURE + (dir.startsWith(File.separator) ? dir : File.separator + dir);

			response.setContentType("application/json; charset=utf-8");
			response.setContentType("application/octet-stream");
			OutputStream outputStream = new BufferedOutputStream(response.getOutputStream());

			// 判断文件是否存在
			boolean existRemoteFile = jGitService.existRemoteFile(repository, tagName, dir);

			// 预览文件readme文件不区分大小写处理
			if (dir.endsWith(Constant.SMALL_README) || dir.endsWith(Constant.BIG_README)) {
				if (!existRemoteFile && dir.endsWith(Constant.SMALL_README)) {
					dir = dir.replace(Constant.SMALL_README, Constant.BIG_README);
					existRemoteFile = jGitService.existRemoteFile(repository, tagName, dir);
				} else if (!existRemoteFile && dir.endsWith(Constant.BIG_README)) {
					dir = dir.replace(Constant.BIG_README, Constant.SMALL_README);
					existRemoteFile = jGitService.existRemoteFile(repository, tagName, dir);
				}
			}

			// 判断仓库文件是否存在
			if (!existRemoteFile) {
				LOGGER.error("previewFile not exists, repository : {}, tagName : {}, dir : {}", repository, tagName, dir);
				return;
			}

			// 下载文件流
			jGitService.downloadRemoteFile(repository, tagName, dir, outputStream);
			outputStream.flush();
			outputStream.close();
		} catch (UnsupportedEncodingException e) {
			LOGGER.error("previewFile UnsupportedEncodingException", e);
		} catch (IOException e) {
			LOGGER.error("previewFile IOException", e);
		}
	}

	/**
	 * @Description 本地仓库文件预览
	 * @param localPath /projects/${userId}/${projectName}/code
	 * @param tagName 标签名
	 * @param dir 搜索路径，空字符串表示树的根
	 * @author lizi
	 * @date 2018/12/18 下午6:43
	 */
	public void previewLocalFile(String localPath, String tagName, String dir, HttpServletResponse response) {
		try {
			// git目录
			String gitPath = localPath.substring(0, localPath.length() - Constant.PROJECT_CODE_STRUCTURE.length());

			// 文件路径处理
			dir = Constant.GIT_CODE_STRUCTURE + (dir.startsWith(File.separator) ? dir : File.separator + dir);

			response.setContentType("application/json; charset=utf-8");
			response.setContentType("application/octet-stream");
			OutputStream outputStream = new BufferedOutputStream(response.getOutputStream());

			// 判断文件是否存在
			boolean existRemoteFile = jGitService.existLocalFile(gitPath, tagName, dir);

			// 预览文件readme文件不区分大小写处理
			if (dir.endsWith(Constant.SMALL_README) || dir.endsWith(Constant.BIG_README)) {
				if (!existRemoteFile && dir.endsWith(Constant.SMALL_README)) {
					dir = dir.replace(Constant.SMALL_README, Constant.BIG_README);
					existRemoteFile = jGitService.existLocalFile(gitPath, tagName, dir);
				} else if (!existRemoteFile && dir.endsWith(Constant.BIG_README)) {
					dir = dir.replace(Constant.BIG_README, Constant.SMALL_README);
					existRemoteFile = jGitService.existLocalFile(gitPath, tagName, dir);
				}
			}

			// 判断仓库文件是否存在
			if (!existRemoteFile) {
				LOGGER.error("previewFile not exists, localPath : {}, tagName : {}, dir : {}", localPath, tagName, dir);
				return;
			}

			// 下载文件流
			jGitService.downloadLocalFile(localPath, tagName, dir, outputStream);
			outputStream.flush();
			outputStream.close();
		} catch (UnsupportedEncodingException e) {
			LOGGER.error("previewFile UnsupportedEncodingException", e);
		} catch (IOException e) {
			LOGGER.error("previewFile IOException", e);
		}
	}

	/**
	 * @Description 文件下载
	 * @param repository 远程仓库地址 git@github.com:${userId}/project1-123123423423423.git
	 * @param tagName 标签名
	 * @param dir 搜索路径，空字符串表示树的根
	 * @author lizi
	 * @date 2019/05/30 下午6:43
	 */
	public void downloadRemoteFile(String repository, String tagName, String dir, OutputStream outputStream) {
		// 文件路径处理
		dir = Constant.GIT_CODE_STRUCTURE + (dir.startsWith(File.separator) ? dir : File.separator + dir);

		jGitService.downloadRemoteFile(repository, tagName, dir, outputStream);
	}

	/**
	 * @Description 文件下载
	 * @param localPath /projects/${userId}/${projectName}/code
	 * @param tagName 标签名
	 * @param dir 搜索路径，空字符串表示树的根
	 * @author lizi
	 * @date 2019/05/30 下午6:43
	 */
	public void downloadLocalFile(String localPath, String tagName, String dir, OutputStream outputStream) {
		// git目录
		String gitPath = localPath.substring(0, localPath.length() - Constant.PROJECT_CODE_STRUCTURE.length());

		// 文件路径处理
		dir = Constant.GIT_CODE_STRUCTURE + (dir.startsWith(File.separator) ? dir : File.separator + dir);

		jGitService.downloadLocalFile(gitPath, tagName, dir, outputStream);
	}

	/**
	 * @Description 远程仓库文件批量下载
	 * @param repository 远程仓库地址 git@github.com:${userId}/project1-123123423423423.git
	 * @param tagName 标签名
	 * @param dirs 搜索路径数组，空字符串表示树的根
	 * @author lizi
	 * @date 2019/05/30 下午6:43
	 */
	public void batchDownloadRemoteFile(String repository, String tagName, String[] dirs, OutputStream outputStream) {
		List<String> dirList = new ArrayList<>();
		if(ArrayUtils.isNotEmpty(dirs)) {
			for(String dir : dirs) {
				// 判断仓库文件是否存在
				dir = Constant.GIT_CODE_STRUCTURE + (dir.startsWith(File.separator) ? dir : File.separator + dir);
				if (!jGitService.existRemoteFile(repository, tagName, dir)) {
					LOGGER.error("batchDownloadFile not exists, repository : {}, tagName : {}, dir : {}", repository, tagName, dir);
					return;
				}
				dirList.add(dir);
			}
		} else {
			// 空字符串表示下载真个仓库
			dirList.add(Constant.GIT_CODE_STRUCTURE);
		}


		if(!dirList.isEmpty()) {
			jGitService.batchDownloadRemoteFile(repository, tagName, dirList.toArray(new String[]{}), outputStream);
		}
	}

	/**
	 * @Description 本地仓库文件批量下载
	 * @param localPath /projects/${userId}/${projectName}/code
	 * @param tagName 标签名
	 * @param dirs 搜索路径数组，空字符串表示树的根
	 * @author lizi
	 * @date 2019/05/30 下午6:43
	 */
	public void batchDownloadLocalFile(String localPath, String tagName, String[] dirs, OutputStream outputStream) {
		// git目录
		String gitPath = localPath.substring(0, localPath.length() - Constant.PROJECT_CODE_STRUCTURE.length());

		List<String> dirList = new ArrayList<>();
		if(ArrayUtils.isNotEmpty(dirs)) {
			for(String dir : dirs) {
				// 判断仓库文件是否存在
				dir = Constant.GIT_CODE_STRUCTURE + (dir.startsWith(File.separator) ? dir : File.separator + dir);
				if (!jGitService.existLocalFile(gitPath, tagName, dir)) {
					LOGGER.error("batchDownloadFile not exists, localPath : {}, tagName : {}, dir : {}", localPath, tagName, dir);
					return;
				}
				dirList.add(dir);
			}
		} else {
			// 空字符串表示下载真个仓库
			dirList.add(Constant.GIT_CODE_STRUCTURE);
		}


		if(!dirList.isEmpty()) {
			jGitService.batchDownloadLocalFile(gitPath, tagName, dirList.toArray(new String[]{}), outputStream);
		}
	}

	/**
	 * @Description 文件预览
	 * @param repository 远程仓库地址 git@github.com:${userId}/project1-123123423423423.git
	 * @param tagName 标签名
	 * @param dir 搜索路径，空字符串表示树的根
	 * @author lizi
	 * @date 2019/05/30 下午6:43
	 */
	public boolean isRemoteDirectory(String repository, String tagName, String dir) {
		// 文件路径处理
		dir = Constant.GIT_CODE_STRUCTURE + (dir.startsWith(File.separator) ? dir : File.separator + dir);

		return jGitService.isRemoteDirectory(repository, tagName, dir);
	}

	/**
	 * @Description 文件预览
	 * @param localPath /projects/${userId}/${projectName}/code
	 * @param tagName 标签名
	 * @param dir 搜索路径，空字符串表示树的根
	 * @author lizi
	 * @date 2019/05/30 下午6:43
	 */
	public boolean isLocalDirectory(String localPath, String tagName, String dir) {
		// git目录
		String gitPath = localPath.substring(0, localPath.length() - Constant.PROJECT_CODE_STRUCTURE.length());

		// 文件路径处理
		dir = Constant.GIT_CODE_STRUCTURE + (dir.startsWith(File.separator) ? dir : File.separator + dir);

		return jGitService.isLocalDirectory(localPath, tagName, dir);
	}

	/**
	 * @Description 遍历仓库某个分支下的所有文件
	 * @param fileList 待处理文件列表
	 * @param pageNumber: 当前页数
	 * @param pageSize: 每页条数
	 * @author suqiliang
	 * @Date 2018年6月29日 上午11:56:52
	 */
	private FilePageDto transferFileListToPage(List<FileInfoDto> fileList, Integer pageNumber, Integer pageSize) {
		// 定义返回结果
		FilePageDto filePage = new FilePageDto(pageNumber, pageSize);

		// 获取当前页数据
		List<FileInfoDto> currentFileInfoDtoList = new ArrayList<>();
		if(!CollectionUtils.isEmpty(fileList)) {
			int currIndex = (pageNumber > 1 ? (pageNumber -1) * pageSize : 0);
			for (int i = 0; i < pageSize && i < fileList.size() - currIndex; i++) {
				FileInfoDto projectJobViewDto = fileList.get(currIndex + i);
				currentFileInfoDtoList.add(projectJobViewDto);
			}
		}

		// 设置分页信息
		filePage.setTotalCount(fileList.size());
		filePage.setFileList(currentFileInfoDtoList);
		filePage.setTotalPage((filePage.getTotalCount() + pageSize - 1) / pageSize);
		return filePage;
	}
}